/*
 * Copyright (c) 2016 Cadence Design Systems, Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief kernel swapper code for Xtensa
 *
 * This module implements the __swap() routine for the Xtensa architecture.
 */

#include <xtensa_context.h>
#include <kernel_arch_data.h>
#include <offsets_short.h>

	.extern _kernel
/* unsigned int __swap (unsigned int basepri); */
	.globl  __swap
	.type   __swap,@function
	.align  4
__swap:
#ifdef __XTENSA_CALL0_ABI__
	addi    sp,  sp, -XT_SOL_FRMSZ
#else
	entry   sp,  XT_SOL_FRMSZ
#endif
	s32i    a0,  sp, XT_SOL_pc
	s32i    a2,  sp, XT_SOL_ps
#ifdef __XTENSA_CALL0_ABI__
	s32i    a12, sp, XT_SOL_a12         /* save callee-saved registers */
	s32i    a13, sp, XT_SOL_a13
	s32i    a14, sp, XT_SOL_a14
	s32i    a15, sp, XT_SOL_a15
#else
	/* Spill register windows. Calling xthal_window_spill() causes extra
	 * spills and reloads, so we will set things up to call the _nw version
	 * instead to save cycles.
	 */
	/* spills a4-a7 if needed */
	movi    a6,  ~(PS_WOE_MASK|PS_INTLEVEL_MASK)
	and     a2,  a2, a6                           /* clear WOE, INTLEVEL */
	addi    a2,  a2, XCHAL_EXCM_LEVEL             /* set INTLEVEL */
	wsr     a2,  PS
	rsync
	call0   xthal_window_spill_nw
	l32i    a2,  sp, XT_SOL_ps                    /* restore PS */
	addi    a2, a2, XCHAL_EXCM_LEVEL
	wsr     a2,  PS
#endif
#if XCHAL_CP_NUM > 0
	/* Save coprocessor callee-saved state (if any). At this point CPENABLE
	 * should still reflect which CPs were in use (enabled).
	 */
	call0   _xt_coproc_savecs
#endif
	movi a2, _kernel
	movi a3,  0
	l32i a4, a2, KERNEL_OFFSET(current) /* a4 := _kernel->current */

	s32i a3,  sp, XT_SOL_exit       /* 0 to flag as solicited frame */
	s32i sp,  a4, THREAD_OFFSET(sp) /* current->arch.topOfStack := sp */
	/*
	 * Set __swap()'s default return code to -EAGAIN. This eliminates the
	 * need for the timeout code to set it itself.
	 */
	movi a3, -11 /* a3 := -EAGAIN. TODO: Use a macro here insted of 11 */
	s32i a3, a4, THREAD_OFFSET(retval) /* current->arch.retval := -EAGAIN */

#if XCHAL_CP_NUM > 0
	/* Clear CPENABLE, also in task's co-processor state save area. */
	movi    a3,  0
	/* a4 = _kernel->current */
	wsr     a3,  CPENABLE
	s16i    a3,  a4, THREAD_OFFSET(cpEnable) /* clear saved cpenable */
#endif

#ifdef CONFIG_TRACING
	/* Register the context switch */
#ifdef __XTENSA_CALL0_ABI__
	call0 z_sys_trace_thread_switched_in
#else
	call4 z_sys_trace_thread_switched_in
#endif
#endif
	/* _thread := _kernel.ready_q.cache */
	l32i a3, a2, KERNEL_OFFSET(ready_q_cache)
	/*
	 * Swap threads if any is to be swapped in.
	 */
	call0   _zxt_dispatch /* (_kernel@a2, _thread@a3) */
	/* Never reaches here. */

